
"""\
<html>
  <body>
  <revision />
<p>AnaTree is treated as the default one to export/import the formatter
tree structure, which is always embedded inside the release with the name
'AnaFile'.</p>

<p>It also contains a formatter to import the atr file to the standard tree in
the core application. If the core application could find the generator plug-in,
It would indiate that it couldn't accomplish the function.</p>

If a plug-in returns a list that doesn't match the format, the application
could ignore the values without any complains. The class <code>Invoker</code>
in <code>ab_executor</code> defines the interfaces for plug-ins to create the
standard formatter lists. More information could refer to the help manual of
the program</p>

<p>AnaTree also supports the import and export with a proprietary <code>atr
</code> format. During the evoluation of <code>atr</code>, only the latest
version could be exported. It's not possible to export an <code>atr</code>
format with old version. The format has the header below, which would be
treated as a comment with the leading hash character.</p>
<p></p>
<code>
<br>## anaTree 2.22
<br>##########################################
</code>

<p>Two hash characters with the plug-in name <code>anaTree</code> following the
generated plug-in version. And a line represents a node in AoF tree view. The
indent value indicates the child status.</p>

<code>[0,0] {X-Mms-Message-Type} {m-send-req (0x80)} (O:0,S:2)</code>

<p>It's a line for <code>atr</code> version 2.</p>
<p></p>
<ul>
<li>The first zero in the bracket saves the <code>extractable</code> of the
    node. A node could be extracted even it's no children.</li>
<li>The second one in the bracket is the <code>attr</code> of the node, which
    is defined in the <code>anaTree</code> specification.</li>
<li>The first parenthesis contains the <code>name</code> of the node.</li>
<li>The second keeps the <code>desc</code> of the node.</li>
<li>The value following <code>O:</code> is the <code>off</code>set.</li>
<li>The value following <code>S:</code> is the <code>size</code>.</li>
</ul>

<p>Current plug-in doesn't handle the escape of the node contents. If the node
has the similar contents in name or description, the plug-in would work
abnormally! This will be solved in the further</p>
  </body>
</html>
"""

__aof__ = ('AnaFile', 'AOF')
__version__ = '2.22'
__extension__ = (('AnaTree Files', ('*.atr',),),)

import os
import re

# The plug-in is a systemic one, thus, it's decided to used the packages
# of anaFile rather than developing the tyre again.

import ab_utils

from ab_field import AtrField
from ab_object import AtrObject

if False:
    _resource = '''
# It's a generator sample for anaFile
range level 1,99,99
'''
else:
    # pylint: disable=C0301
    _resource = '''
<?xml version="1.0"?>
<resource>
    <object class="wxPanel" name="panel_1">
        <style>wxTAB_TRAVERSAL</style>
        <object class="wxFlexGridSizer" name="grid_sizer_1">
            <rows>1</rows>
            <cols>2</cols>
            <vgap>5</vgap>
            <hgap>5</hgap>
            <object class="sizeritem">
                <border>5</border>
                <object class="wxStaticText" name="label_1">
                    <attribute>1</attribute>
                    <label>Level</label>
                </object>
            </object>
            <object class="sizeritem">
                <option>1</option>
                <flag>wxEXPAND|wxALL</flag>
                <border>5</border>
                <object class="wxSpinCtrl" name="Level">
                    <value>99</value>
                </object>
            </object>
        </object>
    </object>
</resource>
'''


class _AnaTreeMultiService:  # pylint: disable=W0232
    VERSION_1_X = 1
    VERSION_2_X = 2

    MAGIC = "## anaTree "

    def Execute(self, source, target, notify, **kws):
        plxtype = kws.get('plxtype')
        if plxtype == 'formatter':
            return self._doFormat(source, target, notify, kws)
        elif plxtype == 'generator':
            return self._doGenerate(source, target, notify, kws)
        else:
            return False

    def _getVersionFromText(self, line, default):
        version = default
        if line.startswith(self.MAGIC) and len(line) >= len(self.MAGIC):
            digit = line[len(self.MAGIC)]
            try:
                version = int(digit)
            except ValueError:
                pass

        return version

    def _doFormat(self, source, target, notify, kws):
        firstline = source.readline()
        if firstline.startswith('%s%d.' % (self.MAGIC, self.VERSION_1_X)) or \
                firstline.startswith('%s%d.' % (self.MAGIC, self.VERSION_2_X)):
            lines = source.readlines()
            return self._doFormatV1_2(
                target, lines, target.getEntry(),
                self._getVersionFromText(firstline, self.VERSION_1_X),
                notify, kws)
        else:
            return False

    # pylint: disable=W0613
    def _doFormatV1_2(self, target, lines, entry, version, notify, kws):
        ret = False

        first, last = dict(), dict()
        first[0] = entry

        for li in lines:
            ln = li.strip()
            # skip blank lines and comments
            if len(ln) == 0:
                continue
            elif ln[0] == '#':
                version = self._getVersionFromText(ln, version)
                continue

            indent = li.find(ln)

            obj = None
            ret = False
            # get the values with a regular expression
            if version == self.VERSION_1_X:
                m = re.match(r'^\s*\[(\d+),(\d+)\] \{(.*)\} \{(.*)\} '
                             r'\(O:(\d+),S:(\d+),C:(\d+),Z:(\d+)\)$', ln)
                if m is None:
                    break
                try:
                    # use the interfaces inside AtrObject to create the
                    # atr format m.group(2) for attribute is ignored now
                    obj = AtrObject(m.group(3), m.group(4),
                                    AtrField(int(m.group(5)),
                                    int(m.group(6))))
                except ValueError:
                    raise

            elif version == self.VERSION_2_X:
                m = re.match(r'\s*\[(\d+),(\d+|\w+)\] \{(.*)\} '
                             r'\{(.*)\}(.*)$', ln)
                if m is None:
                    break

                attr, subject, content, fx = (m.group(2), m.group(3),
                                              m.group(4), m.group(5))

                # handle the fields
                fields = list()
                while fx != '' and len(fx) > 0:
                    m = re.match(r' \(O:(\d+),S:(\d+)\)(?P<next>.*)$', fx)
                    if m is None:
                        m = re.match(
                            r' \(O:(\d+),S:(\d+),C:(\d+),Z:(\d+)\)'
                            r'(?P<next>.*)$', fx)
                    if m is None:
                        break

                    try:
                        fields.append(AtrField(int(m.group(1)),
                                      int(m.group(2))))
                        fx = m.group('next')
                    except (TypeError, ValueError):
                        break

                # set the object attribute
                try:
                    attribute = int(attr, 0)
                except (TypeError, ValueError):
                    attribute = attr

                obj = AtrObject(subject, content, fields, attribute=attribute)

            if obj is None:
                break

            if indent in first:
                first[indent].append(obj)
                for v in first.keys()[:]:
                    if v > indent:
                        del first[v]
            else:
                prev = -1
                for v in last.keys():
                    if prev < v < indent:
                        prev = v

                if prev != -1:
                    last[prev].appendChildren(obj)
                    first[indent] = last[prev]

            last[indent] = obj
            ret = True

        return ret

    def _doGenerate(self, source, target, notify,
                    kws):  # pylint: disable=W0613
        # First, it generates the original tree
        out_str = '## anaTree %s%s' % (__version__, os.linesep) + \
                  '##########################################' + os.linesep

        defaultLevel = 99
        if 'Level' in kws:
            limit = kws.get('Level', 0)
            if limit == 0:
                limit = defaultLevel
        else:
            opts = dict()
            limit = defaultLevel
            _dialog = kws['_i'].dialog
            if _dialog:
                opts = _dialog(_resource)
                if opts is None:
                    return False
                else:
                    limit = opts.get('Level', limit)

        # use to dump the object tree recursively
        def _output(tree, limit, level=0):
            out_str = ''
            if level <= limit and tree is not None:
                subject, content, extract, attr, children, fields = \
                    tree.extract('scevCF')
                content = ab_utils.safestr(content)
                if isinstance(attr, (str, unicode)):
                    out_str += ("  " * level) + "[%d,%s] {%s} {%s}" % \
                        (extract, attr, subject, content)
                else:
                    out_str += ("  " * level) + "[%d,%d] {%s} {%s}" % \
                        (extract, int(str(attr), 0), subject, content)

                for fd in fields:
                    out_str += " (O:%d,S:%d)" % (fd.start, fd.size)
                out_str += os.linesep

                for obj in children or list():
                    out_str += _output(obj, limit, level + 1)

            return out_str

        # put the data in "data"
        tree = kws.get('data', None)
        if isinstance(tree, (list, tuple)):
            for obj in tree:
                out_str += _output(obj, limit)
        else:
            out_str += _output(tree, limit)

        target.write(out_str)

        return True


def Service():
    return _AnaTreeMultiService, \
        'formatter,generator,system,non-source,ctx-tree'
